package net.java.dev.eval;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

public class Equation implements IEquation
{
    /**
     * The root of the three of expressions
     */
    private final Expression rootExpression;

    /**
     * Construct an {@link Equation} that may be used multiple times to
     * evaluate the equation using different sets of variables.
     * @param expression the equation to be parse
     */
    public Equation( String expression )
    {
        this.rootExpression = new Expression( expression );
    }

    /**
     * Evaluate the equation with the given set of expressions or values
     * @param variables the variables to use in the equation
     * @return the result of the equation according to the variables
     */
    @Override
    public BigDecimal eval( Map<String, Expression> variables )
    {
        Map<String, BigDecimal> variablesDecimal = new LinkedHashMap<String, BigDecimal>( );
        
        Iterator<Entry<String, Expression>> iterator = variables.entrySet( ).iterator( );
        while(iterator.hasNext( ))
        {
            Entry<String, Expression> entry = iterator.next( );
            getVariables( entry, variables, variablesDecimal );
        }

        return this.rootExpression.eval( variablesDecimal );
    }

    /**
     * This method retrieves all the variables from the different expressions
     * in order to evaluate the equation in a correct manner.
     * @param entry The entry that which variables need to be found
     * @param variables The variables of the equation
     * @param variablesDecimal The variables retrieved and transformed to BigDecimal
     */
    public void getVariables(Entry<String,Expression> entry, Map<String, Expression> variables, Map<String, BigDecimal> variablesDecimal)
    {
        String variableName = entry.getKey( );
        Expression expr = entry.getValue( );
        String strExpr = expr.toString( );
        Map<String, Expression> temp = new LinkedHashMap<String, Expression>( );
        temp.putAll( variables );
        
        Iterator<Entry<String, Expression>> iterator = temp.entrySet( ).iterator( );
        
        while(iterator.hasNext( ))
        {
            Entry<String, Expression> i = iterator.next( );
                    
            if(strExpr.contains( i.getKey( ) ))
            {
                iterator.remove( );
                getVariables( i, variables, variablesDecimal);
            }
        }
        
        BigDecimal result = expr.eval( variablesDecimal );
        variablesDecimal.put( variableName, result );
    }

    /**
     * Returns the root expression of the equation
     * @return rootExpression the root expresion of the equation.
     */
    public Expression getRootExpression( )
    {
        return rootExpression;
    }
}
